package msu.ml.data.level2;

import java.io.*;
import java.util.*;

/**
 * The Ray class contains all of the pulse
 * volumes that fall along the same radial
 * line in a sweep. This class also contains
 * a great deal of other information about
 * the ray and the radar from which this ray
 * came.
 *
 * @author Reginald M Mead
 * @version 1.0
 */
public class Ray 
{
   public static final float BADVAL = 0x20000;
   public static final float EMPTY = 0x20000; //Same as BADVAL
   public static final float BEAM_WIDTH = .95f;
   public static final float RFVAL = (0x20000-1);

   private IPacket packet;

   /**
    * Create a new Ray from the given packet
    */
   public Ray(IPacket packet)
   {
      if(packet == null)
         System.out.println("Null Packet");
      this.packet = packet;
   }

   public double getAtmosphericAttenuation() 
   { 
      return packet.getAtmosphericAttenuation();
   }

   /**
    * Get the azimuth for this ray (degrees)
    *
    * @return azimuth in degrees
    */
   public double getAzimuth() 
   { 
      double azimuth = packet.getAzimuth();
      return (azimuth < 0.0) ? azimuth + 360.0 : azimuth;
   }

   /**
    * Get the azimuth rate for this ray
    *
    * @return azimuth rate
    */
   public double getAzimuthRate() 
   { 
      return StaticData.getAzimuthRate(packet.getVolumeCoveragePattern(),
         packet.getElevationNumber());
   }

   /**
    * Get the elevation angle for this ray
    *
    * @return elevation angle
    */
   public double getElevation() 
   { 
      return packet.getElevation();
   }

   /**
    * Get the elevation number for this ray
    *
    * @return elevation number
    */
   public int getElevationNumber() 
   { 
      return packet.getElevationNumber();
   }

   /**
    * Get the fix angle for this ray
    *
    * @return fix angle
    */
   public double getFixAngle() 
   { 
      return StaticData.getFixAngle(packet.getVolumeCoveragePattern(),
         packet.getElevationNumber());
   }

   /**
    * Get the frequency for this ray
    *
    * @return frequency
    */
   public double getFrequency() 
   { 
      if(getPulseRepetitionFrequency() == 0 || getNyquistVelocity() == 0)
         return (299792458.0 / .1071 * 1.0e-9f);
      else
         return (299792458.0f / (4.0f * getNyquistVelocity() 
                  / getPulseRepetitionFrequency()) * 1.0e-9f);
   }

   public double getGainConstant() 
   { 
      return packet.getSystemCalibration();
   }

   /**
    * Get the ray number for this ray
    *
    * @return ray number
    */
   public int getNumber() 
   { 
      return packet.getRayNumber();
   }

   /**
    * Get the nyquist velocity for this ray
    *
    * @return nyquist velocity
    */
   public double getNyquistVelocity() 
   { 
      return packet.getNyquistVelocity() / 100.0;
   }

   /**
    * Get the pulse count for this ray
    *
    * @return pulse count
    */
   public int getPulseCount() 
   { 
      return StaticData.getPulseCount(packet.getVolumeCoveragePattern(),
         packet.getElevationNumber());
   }

   /**
    * Get the pulse repetition frequency for this ray
    *
    * @return pulse repetition frequency
    */
   public double getPulseRepetitionFrequency() 
   { 
      double prf = getUnambiguousRange() * 1000.0;
      return (prf == 0.0) ? prf : 299792458.0/(2.0 * prf);
   }

   /**
    * Get the pulse width for this ray
    *
    * @return pulse width
    */
   public double getPulseWidth() 
   { 
      return StaticData.getPulseWidth(packet.getVolumeCoveragePattern());
   }

   /**
    * Get the range to the first pulse
    * volume containing reflectivity data
    * (meters).
    *
    * @return range to first reflectivity bin
    */
   public int getRangeToFirstReflectivityBin() 
   { 
      return packet.getReflectivityRange();
   }

   /**
    * Get the range to the first pulse
    * volume containing spectrum width data
    * (meters).
    *
    * @return range to first spectrum width bin
    */
   public int getRangeToFirstSpectrumWidthBin() 
   { 
      return packet.getDopplerRange();
   }

   /**
    * Get the range to the first pulse
    * volume containing velocity data
    * (meters).
    *
    * @return range to first velocity bin
    */
   public int getRangeToFirstVelocityBin() 
   { 
      return packet.getDopplerRange();
   }

   /**
    * Get the reflectivity value of the
    * pulse volume at the specified index.
    * 
    * @param index index of pulse volume(bin)
    * @return reflectivity value
    */
   public double getReflectivity(int index)
   {
      double [] data = getReflectivityData();
      if(index < 0 || index >= data.length)
         return BADVAL;

      return data[index];
   }

   private double [] rData;
   public double [] getReflectivityData()
   {
      if(rData == null)
         rData = packet.getReflectivityData();
      return rData;
   }

   /**
    * Get the number of reflectivity
    * pulse volumes in this ray
    *
    * @return reflectivity pulse volume count
    */
   public int getReflectivityBinCount() 
   { 
      return packet.getReflectivityGates();
   }

   /**
    * Get the size(depth) of the reflectivity
    * pulse volumes in this ray.
    */
   public int getReflectivityGateSize() 
   { 
      return packet.getReflectivityGateSize();
   }

   /**
    * Get the spectrum width value of the
    * pulse volume at the specified index.
    * 
    * @param index index of pulse volume(bin)
    * @return spectrum width value
    */
   public double getSpectrumWidth(int index)
   {
      double [] data = getSpectrumWidthData();
      if(index < 0 || index >= data.length)
         return BADVAL;

      return data[index];
   }

   private double [] sData;
   public double [] getSpectrumWidthData()
   {
      if(sData == null)
         sData = packet.getSpectrumWidthData();

      return sData;
   }

   /**
    * Get the number of spectrum width
    * pulse volumes in this ray
    *
    * @return spectrum width pulse volume count
    */
   public int getSpectrumWidthBinCount() 
   { 
      return packet.getDopplerGates();
   }

   /**
    * Get the size(depth) of the spectrum width
    * pulse volumes in this ray.
    */
   public int getSpectrumWidthGateSize() 
   { 
      return packet.getDopplerGateSize();
   }

   /**
    * Get the sweep rate for this ray
    *
    * @return the sweep rate (m/s)
    */
   public double getSweepRate() 
   { 
      switch(packet.getVolumeCoveragePattern())
      {
         case 11: return (16.0 / 4.0);
         case 12: return (17.0 / 4.2);
         case 21: return (11.0 / 6.0);
         case 31: return (8.0 / 10.0);
         case 32: return (7.0 / 10.0);
         case 121: return (20.0 / 5.5);
         default: return (0.0); 
      }
   }

   public double getThreshold() 
   { 
      return packet.getSNRThreshold() / 10.0;
   }

   /**
    * Get the unambiguous range for this
    * ray. The unambiguous range is related
    * to the doppler dilema. This range is
    * the farthest distance from the radar
    * at which the integrity of the data can
    * still be assumed.
    *
    * @return unambiguous range(meters)
    */
   public double getUnambiguousRange() 
   { 
      return packet.getUnambiguousRange() / 10.0;
   }

   /**
    * Get the velocity value of the
    * pulse volume at the specified index.
    * 
    * @param index index of pulse volume(bin)
    * @return velocity value
    */
   public double getVelocity(int index)
   {
      double [] data = getVelocityData();

      if(index < 0 || index >= data.length)
         return BADVAL;

      return data[index];
   }

   private double [] vData;
   public double [] getVelocityData()
   {
      if(vData == null)
         vData = packet.getVelocityData();

      return vData;
   }

   /**
    * Get the number of velocity
    * pulse volumes in this ray
    *
    * @return velocity pulse volume count
    */
   public int getVelocityBinCount() 
   { 
      return packet.getDopplerGates();
   }

   /**
    * Get the size(depth) of the velocity
    * pulse volumes in this ray.
    */
   public int getVelocityGateSize() 
   { 
      return packet.getDopplerGateSize();
   }

   /**
    * Get the velocity resolution for this
    * ray.
    *
    * @return the resolution of velocity data
    */
   public double getVelocityResolution() 
   { 
      return packet.getVelocityResolution() == 2 ? 0.5 : 0.0;
   }

   /**
    * Set the reflectivity value of the
    * pulse volume at the specified index.
    * 
    * @param index index of pulse volume(bin)
    * @param value reflectivity value
    */
   public void setReflectivity(int index, double value)
   {
      if(rData == null)
         rData = packet.getReflectivityData();

      rData[index] = value;
   }

   /**
    * Set the spectrum width value of the
    * pulse volume at the specified index.
    * 
    * @param index index of pulse volume(bin)
    * @param value spectrum width value
    */
   public void setSpectrumWidth(int index, double value)
   {
      if(sData == null)
         sData = packet.getSpectrumWidthData();

      sData[index] = value;
   }

   /**
    * Set the velocity value of the
    * pulse volume at the specified index.
    * 
    * @param index index of pulse volume(bin)
    * @param value velocity value
    */
   public void setVelocity(int index, double value)
   {
      if(vData == null)
         vData = packet.getVelocityData();

      vData[index] = value;
   }

   /**
    * Get the reflectivity value of the pulse
    * volume at the specified range (meters)
    *
    * @param range distance to pulse volume
    * @return refelctivity value
    */
   public double getReflectivityAtRange(int range)
   {
      return getReflectivity(getReflectivityRangeIndex(range));
   }

   public int getReflectivityRangeIndex(int range)
   {
      return (range <= 0 ? 0 : range / getReflectivityGateSize());
   }

   /**
    * Get the spectrum width value of the pulse
    * volume at the specified range (meters)
    *
    * @param range distance to pulse volume
    * @return refelctivity value
    */
   public double getSpectrumWidthAtRange(int range)
   {
      return getSpectrumWidth((range == 0 ? 0 : 
               range / getSpectrumWidthGateSize()));
   }

   /**
    * Get the velocity value of the pulse
    * volume at the specified range (meters)
    *
    * @param range distance to pulse volume
    * @return refelctivity value
    */
   public double getVelocityAtRange(int range)
   {
      return getVelocity(getVelocityRangeIndex(range));
   }

   public int getVelocityRangeIndex(int range)
   {
      return (range == 0 ? 0 : range / getVelocityGateSize());
   }

   /**
    * Get the time of this ray
    * 
    * @return time for this ray
    */
   public Date getTime() 
   { 
      long ldate = (long)packet.getRayDate() * 24 * 60 * 60 * 1000;
      return new Date(ldate + packet.getRayTime());
   }

   public static Date convertJulianToDate(int julian)
   {
      return Ray.convertJulianToDate(julian, 0);
   }

   public static Date convertJulianToDate(int julian, long millis)
   {
      return new Date(millis + ((long)julian * 24 * 60 * 60 * 1000));
   }

   /**
    * Get the largest range for which there
    * is reflectivity and doppler information
    *
    * @return largest common range
    */
   public int getLargestCommonRange()
   {
      int vSize = (getVelocityGateSize() * getVelocityBinCount());
      int fSize = (getReflectivityGateSize() * getReflectivityBinCount());
      return (vSize < fSize) ? vSize : fSize;
   }

   /**
    * Get the smallest gate size amongst
    * the reflectivity and doppler gates.
    *
    * @return smallest gate size
    */
   public int getSmallestGateSize()
   {
      return (getVelocityGateSize() < getReflectivityGateSize()) 
         ? getVelocityGateSize() : getReflectivityGateSize();
   }

   /**
    * Get the volume coverage pattern of this ray
    * 
    * @return volume coverage pattern for this ray
    */
   public int getVolumeCoveragePattern() 
   { 
      return packet.getVolumeCoveragePattern();
   }

   /**
    * Get a string representation of this ray
    */
   public String toString()
   {
      return "Azimuth: " + getAzimuth() + "\n" +
         "Elevation: " + getElevation() + "\n" +
         "First Reflectivity Bin: " + 
         getRangeToFirstReflectivityBin() + "m\n" +
         "Reflectivity Bins: " + getReflectivityBinCount() + "\n" +
         "Reflectivity Gate Size: " + getReflectivityGateSize() + "\n" +
         "Velocity Bins: " + getVelocityBinCount() + "\n" +
         "Velocity Gate Size: " + getVelocityGateSize() + "\n" +
         "SpectrumWidth Bins: " + getSpectrumWidthBinCount() + "\n" +
         "SpectrumWidth Gate Size: " + getSpectrumWidthGateSize() + "\n" +
         "Pulse Repetition Frequency: " + getPulseRepetitionFrequency() + "\n" +  
         "Frequency: " + getFrequency();
   }

}
